#ifndef CONFIG_JS_H
#define CONFIG_JS_H

#include "config.h"
#include "config_base.h"

#include <QObject>
#include <QtScript>
#include <QFile>
#include <QTextStream>

//////////////////////////////////////////////////////////////////////////

#define DEF_PROPERTY(name, prop) \
    QString get_##name(void)            { return (prop); } \
    void set_##name(const QString& val) { (prop) = val; }

#define DEF_PROPLIST(name, prop, ...) \
    QString get_##name(void) \
    { \
        QString cons; \
        foreach (QString con, prop) cons += "\'" + con + "\' "; \
        return cons; \
    } \
    void set_##name(const QString& val) \
    { \
        prop.clear(); \
        intercept_list(val, prop,##__VA_ARGS__); \
    }

//////////////////////////////////////////////////////////////////////////

class JSSolution : public QObject
{
    Q_OBJECT

    Q_PROPERTY(QString name          READ get_name          WRITE set_name)
    Q_PROPERTY(QString project_path  READ get_project_path  WRITE set_project_path)
    Q_PROPERTY(QString include_path  READ get_include       WRITE set_include)
    Q_PROPERTY(QString tmp_path      READ get_tmp_path      WRITE set_tmp_path)
    Q_PROPERTY(QString out_path      READ get_out_path      WRITE set_out_path)
    Q_PROPERTY(QString DEFINES       READ get_defines       WRITE set_defines)
    Q_PROPERTY(QString CFLAGS        READ get_cflags        WRITE set_cflags)
    Q_PROPERTY(QString LFLAGS        READ get_lflags        WRITE set_lflags)

    DEF_PROPERTY(name,         Config::solution().name_)
    DEF_PROPERTY(project_path, path_file_.path_)
    DEF_PROPERTY(tmp_path,     Config::solution().tmp_path_)
    DEF_PROPERTY(out_path,     Config::solution().out_path_)
    DEF_PROPERTY(cflags,       Config::solution().CFLAGS_)
    DEF_PROPERTY(lflags,       Config::solution().LFLAGS_)

    QString get_include(void)
    {
        QString cons;
        foreach (QString con, Config::solution().include_paths_)
            cons += "\'" + con + "\' ";
        return cons;
    }
    void set_include(const QString& val)
    {
        Config::solution().include_paths_.clear();
        Config::solution().include_paths_.append(path_file_.path_);
        intercept_list(val, Config::solution().include_paths_, path_file_);
    }

    DEF_PROPLIST(defines, Config::solution().DEFINES_)

public:
    JSSolution(PathAppendFile_& path_file)
        : path_file_(path_file)
    {}

private:
    PathAppendFile_& path_file_;
};

//////////////////////////////////////////////////////////////////////////

class JSProject : public QObject
{
    Q_OBJECT

    Q_PROPERTY(QString name     READ get_name    WRITE set_name)
    Q_PROPERTY(QString GUID     READ get_guid    WRITE set_guid)
    Q_PROPERTY(QString type     READ get_type    WRITE set_type)
    Q_PROPERTY(QString libs     READ get_libs    WRITE set_libs)
    Q_PROPERTY(QString depends  READ get_depends WRITE set_depends)
    Q_PROPERTY(QString heads    READ get_heads   WRITE set_heads)
    Q_PROPERTY(QString sources  READ get_sources WRITE set_sources)

    DEF_PROPERTY(name,    prj_.name_)
    DEF_PROPERTY(guid,    prj_.GUID_)
    DEF_PROPERTY(type,    prj_.type_)
    DEF_PROPERTY(libs,    prj_.libs_)

    DEF_PROPLIST(depends, prj_.depends_)
    DEF_PROPLIST(heads,   prj_.heads_  , path_file_)
    DEF_PROPLIST(sources, prj_.sources_, path_file_)

public:
    explicit JSProject(QObject* parent, Project& prj, PathAppendFile_& path_file)
        : QObject(parent), prj_(prj), path_file_(path_file)
    {}

private:
    Project& prj_;
    PathAppendFile_& path_file_;
};

class JSProjects : public QObject
{
    Q_OBJECT

public:
    Q_INVOKABLE QScriptValue create(const QString& name)
    {
        Project prj; prj.name_ = name;
        Config::projects().append(prj);
        return resolver_.newQObject(new JSProject(this, Config::projects().back(), path_file_));
    }

public:
    explicit JSProjects(PathAppendFile_& path_file, QScriptEngine& resolver)
        : path_file_(path_file), resolver_(resolver)
    {}

private:
    PathAppendFile_& path_file_;
    QScriptEngine&   resolver_;
};

//////////////////////////////////////////////////////////////////////////

class JSDefines : public QObject
{
    Q_OBJECT

    Q_PROPERTY(QString PLAT READ get_plat)
    Q_PROPERTY(QString CC   READ get_cc)

public:
    QString get_plat(void) { return plat_; }
    QString get_cc(void)   { return cc_; }

public:
    QString plat_, cc_;
};

//////////////////////////////////////////////////////////////////////////

class ConfigJS: public ConfigBase
{
protected:
    QScriptEngine   resolver_;
    JSDefines       defines_;

public:
    ConfigJS(const QString& plat, const QString& cc)
    {
        defines_.plat_ = plat;
        defines_.cc_   = cc;
        resolver_.globalObject().setProperty("Make", resolver_.newQObject(&defines_));
    }

    bool load(const QString& script_path)
    {
        QFile script_file(script_path);
        if (!script_file.open(QIODevice::ReadOnly)) return false;
        QTextStream stream(&script_file);
        QString contents = stream.readAll();
        script_file.close();

        PathAppendFile_ path_file;
        JSSolution js_solution(path_file);
        JSProjects js_projects(path_file, resolver_);

        resolver_.globalObject().setProperty("Solution", resolver_.newQObject(&js_solution));
        resolver_.globalObject().setProperty("Projects", resolver_.newQObject(&js_projects));

        QScriptValue result = resolver_.evaluate(contents, script_path);
        if (result.isError())
        {
            QTextStream(stdout)
                << "\nScript Error:" << "\n"
                << script_path << ":"
                << result.property("lineNumber").toInt32() <<  " ->: "
                << result.toString() << "\n";
            return false;
        }
        return true;
    }
};

//////////////////////////////////////////////////////////////////////////

#endif // CONFIG_JS_H
